﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using RegexLib.Web.Models.Data;

namespace RegexLib.Web.Models.Web
{
	/// <summary>
	/// 表示正则表达式库，提供核心功能。
	/// </summary>
	public static class RxLibrary
	{
		/// <summary>
		/// 添加一个正则表达式。
		/// </summary>
		/// <param name="ownerUser">正则表达式的所有者。</param>
		/// <param name="title">标题。</param>
		/// <param name="accessKey">访问路径。</param>
		/// <param name="description">描述。</param>
		/// <param name="pattern">模式串。</param>
		/// <param name="keywords">关键词。请确保没有重复项。</param>
		/// <param name="ip">用户添加此正则时所在的IP地址。</param>
		/// <returns>已经添加到数据库中的正则表达式。</returns>
		public static RegexInfo AddRegex(UserInfo ownerUser, string title, string accessKey, string description, string pattern, string[] keywords, string ip)
		{
			if (ownerUser == null)
				throw new ArgumentNullException("ownerUser");

			if (title == null)
				throw new ArgumentNullException("title");

			if (description == null)
				throw new ArgumentNullException("description");

			if (pattern == null)
				throw new ArgumentNullException("pattern");

			// 构建正则表达式数据对象
			RegexInfo reg = new RegexInfo();
			reg.RegexId = Guid.NewGuid();
			reg.Title = title;
			reg.Description = description;
			reg.Pattern = pattern;
			reg.UserId = ownerUser.UserId;
			reg.AddedTime = DateTime.Now;
			reg.UserIP = ip ?? "";
			reg.AccessKey = string.IsNullOrEmpty(accessKey) ? reg.RegexId.ToString() : accessKey;

			using (RxDataContext db = new RxDataContext())
			{
				var q = db.RegexInfos.FirstOrDefault(r => r.AccessKey == accessKey);

				if (q != null)
					throw new RxRegexAccessKeyExistsException();

				db.RegexInfos.InsertOnSubmit(reg);

				// 添加关键字
				if (keywords != null && keywords.Length > 0)
				{
					foreach (string keyword in keywords)
					{
						var key = db.KeywordInfos.FirstOrDefault(k=>k.LoweredText == keyword.ToLower());

						// 如果这个关键字还没人用过，则以ownerUser的名义加到系统中
						if (key == null)
						{
							key = new KeywordInfo();
							key.KeywordId = Guid.NewGuid();
							key.Text = keyword;
							key.LoweredText = keyword.ToLower();
							key.ContributedUserId = ownerUser.UserId;
							key.FirstAddedTime = reg.AddedTime;

							db.KeywordInfos.InsertOnSubmit(key);
						}

						// 将关键字与正则表达式关联起来。
						RegexKeyword rk = new RegexKeyword();
						rk.RegexId = reg.RegexId;
						rk.KeywordId = key.KeywordId;
						rk.UserId = ownerUser.UserId;
						rk.AddedTime = reg.AddedTime;

						db.RegexKeywords.InsertOnSubmit(rk);
					}
				}

				db.SubmitChanges();
			}

			return reg;
		}

		/// <summary>
		/// 根据路径获取正则表达式对象。
		/// </summary>
		/// <param name="path">路径，或者是GUID，或者是AccessKey。</param>
		/// <returns>具有指定路径的正则表达式；如果找不到，返回null。</returns>
		public static RegexInfo GetRegexByPath(string path)
		{
			if (path.IsGuid())  // 按照guid查找
			{
				return GetRegexById(new Guid(path));
			}
			else  // 按照AccessKey查找
			{
				return GetRegexByAccessKey(path);
			}
		}

		/// <summary>
		/// 根据ID获取正则表达式对象。
		/// </summary>
		/// <param name="id">正则表达式的ID。</param>
		/// <returns>具有指定ID的正则表达式；如果找不到，返回null。</returns>
		public static RegexInfo GetRegexById(Guid id)
		{
			using (RxDataContext db = new RxDataContext())
			{
				return db.RegexInfos.FirstOrDefault(r => r.RegexId == id);
			}
		}

		/// <summary>
		/// 根据访问路径获取正则表达式对象。
		/// </summary>
		/// <param name="accessKey">访问路径。</param>
		/// <returns>具有指定访问路径的正则表达式；如果找不到，返回null。</returns>
		public static RegexInfo GetRegexByAccessKey(string accessKey)
		{
			using (RxDataContext db = new RxDataContext())
			{
				return db.RegexInfos.FirstOrDefault(r => r.AccessKey == accessKey);
			}
		}

		/// <summary>
		/// 获取最新的正则表达式。
		/// </summary>
		/// <param name="number">获取的数量。</param>
		/// <returns></returns>
		public static RegexInfo[] GetLastedRegex(int number)
		{
			if (number <= 0)
				throw new ArgumentException("获取数量必需大于0。", "number");

			using (RxDataContext db = new RxDataContext())
			{
				return db.RegexInfos.OrderByDescending(r => r.AddedTime).Take(number).ToArray();
			}
		}

		/// <summary>
		/// 获取用户添加的正则表达式。
		/// </summary>
		/// <param name="user"></param>
		/// <param name="pageIndex"></param>
		/// <param name="pageSize"></param>
		/// <param name="totalPages"></param>
		/// <returns></returns>
		public static RegexInfo[] GetUserRegex(this UserInfo user, int pageIndex, int pageSize, out int totalPages)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var rxs = db.RegexInfos.Where(r => r.UserId == user.UserId).OrderByDescending(r => r.AddedTime);
				totalPages = (rxs.Count() + pageSize - 1) / pageSize;
				if (pageIndex <= 0)
					pageIndex = totalPages;

				return rxs.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();
			}
		}

		/// <summary>
		/// 编辑一个正则表达式。
		/// </summary>
		/// <param name="id">正则表达式的ID。</param>
		/// <param name="title">正则表达式的标题。</param>
		/// <param name="pattern">正则表达式模式。</param>
		/// <param name="description">对正则表达式的介绍。</param>
		public static void EditRegex(Guid id, string title, string pattern, string description)
		{
			if (title == null)
				throw new ArgumentNullException("title");

			if (pattern == null)
				throw new ArgumentNullException("pattern");

			if (description == null)
				throw new ArgumentNullException("description");

			using (RxDataContext db = new RxDataContext())
			{
				RegexInfo regex = db.RegexInfos.FirstOrDefault(r => r.RegexId == id);
				if (regex == null)
					return;

				regex.Title = title;
				regex.Pattern = pattern;
				regex.Description = description;

				db.SubmitChanges();
			}
		}

		/// <summary>
		/// 为正则表达式添加评论。
		/// </summary>
		/// <param name="user">添加评论的用户。</param>
		/// <param name="regex">评论的正则表达式。</param>
		/// <param name="body">评论内容。</param>
		/// <param name="ip">用户所在的IP地址。</param>
		/// <returns>新添加的评论。</returns>
		public static CommentInfo AddComment(UserInfo user, RegexInfo regex, string body, string ip)
		{
			if (user == null)
				throw new ArgumentNullException("user");

			if (regex == null)
				throw new ArgumentNullException("regex");

			if (string.IsNullOrEmpty(body))
				throw new ArgumentNullException("body");

			CommentInfo comment = new CommentInfo();
			comment.CommentId = Guid.NewGuid();
			comment.RegexId = regex.RegexId;
			comment.UserId = user.UserId;
			comment.AddedTime = DateTime.Now;
			comment.Body = body;
			comment.UserIP = ip;

			using (RxDataContext db = new RxDataContext())
			{
				db.CommentInfos.InsertOnSubmit(comment);
				db.SubmitChanges();
			}

			return comment;
		}

		/// <summary>
		/// 获取正则表达式的评论。
		/// </summary>
		/// <param name="regex"></param>
		/// <param name="pageIndex">第几页，从1开始计算。-1表示获取最后一页。</param>
		/// <param name="pageSize">每页显示几个。</param>
		/// <param name="totalPages">总共有多少页。</param>
		/// <returns></returns>
		public static CommentInfo[] GetComments(this RegexInfo regex, int pageIndex, int pageSize, out int totalPages)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var q = from c in db.CommentInfos
						where c.RegexId == regex.RegexId
						orderby c.AddedTime
						select c;

				totalPages = (q.Count() + pageSize - 1) / pageSize;
				if (pageIndex == -1)
					pageIndex = totalPages;

				int skip = (pageIndex - 1) * pageSize;
				int take = pageSize;

				return q.Skip(skip).Take(take).ToArray();
			}
		}

		/// <summary>
		/// 获取最新评论。
		/// </summary>
		/// <param name="number">获取的数量。如果大于0则取指定数量的最新评论；如果小于等于0，则取LastComment视图中的所有数据（该视图有top选项）。</param>
		/// <returns></returns>
		public static LastComment[] GetLastComments(int number)
		{
			using (RxDataContext db = new RxDataContext())
			{
				if (number > 0)
					return db.LastComments.Take(number).ToArray();
				else
					return db.LastComments.ToArray();
			}
		}

		/// <summary>
		/// 为正则表达式添加关键词。
		/// </summary>
		/// <param name="regex">待添加关键词的正则表达式。</param>
		/// <param name="user">添加关键词的用户。</param>
		/// <param name="keywords">关键词。请注意确保不要有重复项。</param>
		public static void AddKeyword(this RegexInfo regex, UserInfo user, string[] keywords)
		{
			if (regex == null)
				throw new ArgumentNullException("regex");

			if (user == null)
				throw new ArgumentNullException("user");

			if (keywords == null)
				throw new ArgumentNullException("keywords");

			using (RxDataContext db = new RxDataContext())
			{
				foreach (string keyword in keywords)
				{
					string lowkey = keyword.ToLower();

					// 是否已为该正则表达式添加了关键词？
					// 如果添加了就不再添加
					var q = from k in db.KeywordInfos
							join rk in db.RegexKeywords on k.KeywordId equals rk.KeywordId
							where rk.RegexId == regex.RegexId && rk.UserId == user.UserId && k.LoweredText == lowkey
							select k;

					if (q.Count() > 0)
						continue;

					// 如果尚无指定的关键字，添加之
					KeywordInfo key = db.KeywordInfos.FirstOrDefault(k => k.LoweredText == lowkey);

					if (key == null)
					{
						key = new KeywordInfo();
						key.KeywordId = Guid.NewGuid();
						key.Text = keyword;
						key.LoweredText = lowkey;
						key.ContributedUserId = user.UserId;
						key.FirstAddedTime = DateTime.Now;

						db.KeywordInfos.InsertOnSubmit(key);
					}

					// 将关键词添加到正则表达式
					RegexKeyword rrk = new RegexKeyword();
					rrk.RegexId = regex.RegexId;
					rrk.KeywordId = key.KeywordId;
					rrk.UserId = user.UserId;
					rrk.AddedTime = DateTime.Now;

					db.RegexKeywords.InsertOnSubmit(rrk);

					db.SubmitChanges();
				}
			}
		}

		/// <summary>
		/// 移除正则表达式上的关键词。
		/// </summary>
		/// <param name="regexId"></param>
		/// <param name="keywordId"></param>
		/// <param name="userId"></param>
		public static void RemoveKeyword(Guid regexId, Guid keywordId, Guid userId)
		{
			using (RxDataContext db = new RxDataContext())
			{
				RegexKeyword rrk = db.RegexKeywords.FirstOrDefault(rk => rk.RegexId == regexId && rk.KeywordId == keywordId && rk.UserId == userId);

				if (rrk != null)
				{
					db.RegexKeywords.DeleteOnSubmit(rrk);
					db.SubmitChanges();
				}
			}
		}

		/// <summary>
		/// 获取一个正则表达式的作者为其添加的关键字。
		/// </summary>
		/// <param name="regex"></param>
		/// <returns></returns>
		public static KeywordInfo[] GetAuthorKeywords(this RegexInfo regex)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var kq = from rk in db.RegexKeywords
						 join k in db.KeywordInfos on rk.KeywordId equals k.KeywordId
						 where rk.RegexId == regex.RegexId && rk.UserId == regex.UserId
						 select k;

				return kq.ToArray();
			}
		}

		/// <summary>
		/// 获取某个用户为一个正则表达式添加的关键字。
		/// </summary>
		/// <param name="regex"></param>
		/// <param name="user">给定的用户。</param>
		/// <returns></returns>
		public static KeywordInfo[] GetCutomerKeywords(this RegexInfo regex, UserInfo user)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var kq = from rk in db.RegexKeywords
						 join k in db.KeywordInfos on rk.KeywordId equals k.KeywordId
						 where rk.RegexId == regex.RegexId && rk.UserId == user.UserId
						 orderby k.Text
						 select k;

				return kq.ToArray();
			}
		}

		/// <summary>
		/// 获取用户贡献。
		/// </summary>
		/// <param name="number">获取的数量。</param>
		/// <returns></returns>
		public static UserContribution[] GetUserContribution(int number)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var q = from ur in db.UserRegexes
						group ur by new { ur.UserId, ur.Nickname, ur.UserAccessKey } into g
						select new UserContribution
						{
							UserId = g.Key.UserId,
							Nickname = g.Key.Nickname,
							AccessKey = g.Key.UserAccessKey,
							RegexCount = g.Count()
						};
				return q.OrderByDescending(uc => uc.RegexCount).Take(number).ToArray();
			}
		}

		/// <summary>
		/// 获取用户贡献。
		/// </summary>
		/// <param name="pageIndex"></param>
		/// <param name="pageSize"></param>
		/// <param name="totalPages"></param>
		/// <returns></returns>
		public static UserContribution[] GetUserContribution(int pageIndex, int pageSize, out int totalPages)
		{
			using (RxDataContext db = new RxDataContext())
			{
				var q = from ur in db.UserRegexes
						group ur by new { ur.UserId, ur.Nickname, ur.UserAccessKey } into g
						select new UserContribution
						{
							UserId = g.Key.UserId,
							Nickname = g.Key.Nickname,
							AccessKey = g.Key.UserAccessKey,
							RegexCount = g.Count()
						};
				int count = q.Count();
				totalPages = (count + pageSize - 1) / pageSize;

				return q.OrderByDescending(uc => uc.RegexCount).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArray();
			}
		}

		/// <summary>
		/// 表示用户的贡献。
		/// </summary>
		public class UserContribution
		{
			/// <summary>
			/// 用户ID。
			/// </summary>
			public Guid UserId { get; set; }

			/// <summary>
			/// 用户昵称。
			/// </summary>
			public string Nickname { get; set; }

			/// <summary>
			/// 用户的访问路径。
			/// </summary>
			public string AccessKey { get; set; }

			/// <summary>
			/// 用户发布的正则表达式数量。
			/// </summary>
			public int RegexCount { get; set; }
		}
	}

	/// <summary>
	/// 表示正则表达式的访问路径已经存在。
	/// </summary>
	public class RxRegexAccessKeyExistsException : Exception
	{
	}
}
